{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [],
   "source": [
    "%matplotlib inline\n",
    "import numpy as np\n",
    "import matplotlib.pyplot as plt\n",
    "import scipy.signal"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [],
   "source": [
    "def gain(sps):\n",
    "    # Define some constants\n",
    "    N_bits = 30000 # Number of bits used for timing error analysis\n",
    "    baud_rate = 9600\n",
    "    N_syms = N_bits # Number of symbols used for timing error analysis\n",
    "    tea_sps = 100 # samples/symbol used for timing error analysis\n",
    "    tea_Fs = tea_sps * baud_rate # sample rate used for timing error analysis\n",
    "    a_sps = sps # actual samples per symbol used in application\n",
    "    a_Fs = a_sps * baud_rate # actual sample rate used in application\n",
    "\n",
    "    # Create rectangular pulse filter\n",
    "    pf_taps = 1/tea_sps * np.ones(tea_sps)\n",
    "    pf_len = pf_taps.size\n",
    "    pf_delay = pf_len/2\n",
    "\n",
    "    # Generate some bits\n",
    "    bits = np.random.randint(0, 2, N_bits)\n",
    "\n",
    "    # Convert the bits to NRZ symbols in [-1, 1]\n",
    "    symbols = 2*bits-1;\n",
    "\n",
    "    # Convert symbols to baseband pulses\n",
    "    x = np.zeros((N_syms * tea_sps))\n",
    "    x[::tea_sps] = tea_sps * symbols\n",
    "    baseband = scipy.signal.lfilter(pf_taps, 1, x)\n",
    "\n",
    "    # Create rectangular matched filter,\n",
    "    # that's a little too long (7 sps vs 6.667 sps), to introduce more ISI\n",
    "    #isi_sps = round((tea_sps/a_sps * 7) - tea_sps) # Play with isi samples to see S-curve effects\n",
    "    isi_sps = 0\n",
    "    if isi_sps % 2 != 0:\n",
    "        isi_sps = isi_sps + 1\n",
    "\n",
    "    mf_taps = 1/(tea_sps+isi_sps) * np.ones(tea_sps+isi_sps)\n",
    "    mf_len = mf_taps.size\n",
    "    mf_delay = mf_len/2\n",
    "\n",
    "    # Matched filter the received baseband pulses\n",
    "    mf_baseband = scipy.signal.lfilter(mf_taps, 1, baseband);\n",
    "    mf_baseband = mf_baseband * 1.0 # Play with amplitude to see S-curve effects\n",
    "\n",
    "\n",
    "    # Symbol centers are now at indices n*tea_sps + isi_sps/2 (I think!)\n",
    "    # Symbol clock period is tea_sps samples\n",
    "    # Symbol peaks are perfectly at +/-1.0\n",
    "\n",
    "    # Timing offset granularity is in 1/tea_sps-th of a symbol\n",
    "    tau = np.arange(-tea_sps//2+2, tea_sps//2)\n",
    "    tau_norm = tau/tea_sps*a_sps\n",
    "\n",
    "    # M&M TED. Contellation points at -1.0 and +1.0.\n",
    "    # Gardener TED.\n",
    "    # Perfect estimate of symbol clock period.\n",
    "    # No external noise.\n",
    "    mm_ted_output = np.zeros((N_syms, tau.size));\n",
    "    ga_ted_output = np.zeros((N_syms, tau.size));\n",
    "    # For each known symbol peak set (M&M needs prev and current symbol)\n",
    "    for i in range(1, N_syms-1):\n",
    "        # Cycle through all the timing offsets around this symbol\n",
    "        # using a perfect symbol clock period estimate\n",
    "        opt_prev_idx = (i-1)*tea_sps + isi_sps//2;\n",
    "        opt_curr_idx = i*tea_sps + isi_sps//2;\n",
    "        for j, t in enumerate(tau):\n",
    "            prev_soft_sym = mf_baseband[opt_prev_idx - t];\n",
    "            mid_soft_samp = mf_baseband[(opt_curr_idx+opt_prev_idx)//2 - t];\n",
    "            curr_soft_sym = mf_baseband[opt_curr_idx - t];\n",
    "            prev_decision = 1 if prev_soft_sym >= 0 else -1\n",
    "            curr_decision = 1 if curr_soft_sym >= 0 else -1\n",
    "            mm_ted_output[i,j] = prev_decision * curr_soft_sym - curr_decision * prev_soft_sym\n",
    "            ga_ted_output[i,j] = (prev_soft_sym - curr_soft_sym) * mid_soft_samp\n",
    "        \n",
    "    mean_mm_ted_output = np.average(mm_ted_output, axis = 0)\n",
    "    mean_ga_ted_output = np.average(ga_ted_output, axis = 0)\n",
    "\n",
    "    # Plot the S-Curves\n",
    "    #plt.figure()\n",
    "    ##plt.plot()\n",
    "    ##plt.plot(tau_norm, mean_mm_ted_output)\n",
    "    ##plt.plot(tau_norm, mean_ga_ted_output)\n",
    "    #title('S-Curve, 6.667 Samples/Symbol, Rectangular Pulses, Imperfect MF, E_s/N_0 = \\infty');\n",
    "    #xlabel('Timing Error from Symbol Center, \\tau_\\epsilon (samples)');\n",
    "    #ylabel('Expected Value of TED Output, E(e[n] | \\tau_\\epsilon)');\n",
    "    #grid on;\n",
    "\n",
    "    # Plot the TED gains\n",
    "    ##plt.figure()\n",
    "    tau_diff = tau[:-1]+0.5;\n",
    "    tau_diff_norm = tau_diff/tea_sps*a_sps;\n",
    "    diff_mm_ted_output = np.diff(mean_mm_ted_output)/(a_sps/tea_sps);\n",
    "    diff_ga_ted_output = np.diff(mean_ga_ted_output)/(a_sps/tea_sps);\n",
    "    ##plt.plot(tau_diff_norm, diff_mm_ted_output)#, '- .b;M and M TED;', ...\n",
    "    ##plt.plot(tau_diff_norm, diff_ga_ted_output)#, '- +r;Gardner TED;');\n",
    "    #title('TED Gain, 6.667 Samples/Symbol, Rectangular Pulses, Imperfect MF, E_s/N_0 = \\infty');\n",
    "    #xlabel('Timing Error from Symbol Center, \\tau_\\epsilon (samples)');\n",
    "    #ylabel('Timing Error Detector Gain, Slope of E(e[n] | \\tau_\\epsilon), (sample^{-1})');\n",
    "    #grid on;\n",
    "\n",
    "    # Print out the central TED gains\n",
    "    k = diff_mm_ted_output.size\n",
    "    mm_ted_gain = np.average(diff_mm_ted_output[(k-1)//2:(k-1)//2+2])\n",
    "    k = diff_ga_ted_output.size\n",
    "    ga_ted_gain = np.average(diff_ga_ted_output[(k-1)//2:(k-1)//2+2])\n",
    "    \n",
    "    return (mm_ted_gain*sps, ga_ted_gain*sps)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [],
   "source": [
    "sps = np.arange(2,10.25,0.25)\n",
    "gains = [gain(s) for s in sps]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "<matplotlib.legend.Legend at 0x7f2f19eae160>"
      ]
     },
     "execution_count": 4,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "plt.plot(sps, np.array([g[0] for g in gains]))\n",
    "plt.plot(sps, np.array([g[1] for g in gains]))\n",
    "plt.title('TED gain')\n",
    "plt.ylabel('TED gain (1/symbol)')\n",
    "plt.xlabel('Samples per symbol')\n",
    "plt.legend(['M&M TED', 'Gardner TED'])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "1.4733694343435606"
      ]
     },
     "execution_count": 5,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "np.average(np.array([g[1] for g in gains]))"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.6.9"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
